#include "./SYSTEM/delay/delay.h"
#include "./BSP/ADC/bsp_adc.h"

/* -------------------------------------------- 全局变量定义 -------------------------------------------- */

ADC_HandleTypeDef g_adc_handle;         /* ADC句柄 */

/* 单通道ADC采集 DMA读取 */
DMA_HandleTypeDef g_dma_adc_handle;     /* 与ADC关联的DMA句柄 */
uint8_t g_adc_dma_sta = 0;              /* DMA传输状态标志, 0,未完成; 1, 已完成 */

/* ADC数据存储 */
uint16_t g_adc_values[ADC_CH_NUM];      /* 存储各通道ADC原始值 */
float g_adc_voltages[ADC_CH_NUM];       /* 存储各通道电压值 */
float g_actual_values[ADC_CH_NUM];      /* 存储转换后的实际物理量(V或mA) */
float g_vdda_voltage = 2.5f;            /* 存储VDDA电压值，默认为2.5V */
static uint8_t g_vdda_calibrated = 0;   /* VDDA校准标志，0表示未校准，1表示已校准 */

/* ADC DMA缓冲区 */
#define ADC_DMA_BUF_SIZE        50 * ADC_CH_NUM /* ADC DMA采集 BUF大小，应等于ADC通道数的整数倍 */
uint16_t g_adc_dma_buf[ADC_DMA_BUF_SIZE];       /* ADC DMA BUF */

/* -------------------------------------------- ADC基础功能函数 ---------------------------------------- */

/**
 * @brief       ADC初始化函数
 * @param       无
 * @note        配置ADC的基本参数:
 *              - 支持ADC1/ADC2任意通道,不支持ADC3
 *              - 时钟分频: PCLK2/4 = 84MHz/4 = 21MHz
 *              - 转换精度: 12位 (0~4095)
 *              - 数据对齐: 右对齐
 *              - 扫描模式: 禁用 (单通道转换)
 *              - 连续转换: 使能 (适用于DMA传输)
 *              - 触发方式: 软件触发
 *              - DMA请求: 使能
 *
 *              转换时间计算:
 *              - 采样时间: 480个ADC周期
 *              - 转换时间: 12个ADC周期
 *              - 总时间: (480+12)/21MHz ≈ 23.4μs
 * @retval      无
 */
void adc_init(void)
{
    g_adc_handle.Instance = ADC_ADCX;
    g_adc_handle.Init.ClockPrescaler = ADC_CLOCKPRESCALER_PCLK_DIV4;        /* 4分频，ADCCLK = PCLK2/4 = 84/4 = 21Mhz */
    g_adc_handle.Init.Resolution = ADC_RESOLUTION_12B;                      /* 12位模式 */
    g_adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;                      /* 右对齐 */
    g_adc_handle.Init.ScanConvMode = DISABLE;                               /* 非扫描模式 */
    g_adc_handle.Init.ContinuousConvMode = ENABLE;                          /* 开启连续转换 */
    g_adc_handle.Init.NbrOfConversion = 1;                                  /* 1个转换在规则序列中，也就是只转换规则序列1 */
    g_adc_handle.Init.DiscontinuousConvMode = DISABLE;                      /* 禁止不连续采样模式 */
    g_adc_handle.Init.NbrOfDiscConversion = 0;                              /* 不连续采样通道数为0 */
    g_adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;                /* 软件触发 */
    g_adc_handle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; /* 使用软件触发 */
    g_adc_handle.Init.DMAContinuousRequests = ENABLE;                       /* 开启DMA请求 */
    HAL_ADC_Init(&g_adc_handle);                                            /* 初始化 */
}

/**
 * @brief       ADC底层驱动，引脚配置，时钟使能
 * @note        此函数会被HAL_ADC_Init()调用
 * @param[in]   hadc: ADC句柄指针
 * @retval      无
 */
//void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
//{
//    if(hadc->Instance == ADC_ADCX)
//    {
//        GPIO_InitTypeDef gpio_init_struct;
//        ADC_ADCX_CHY_CLK_ENABLE();      /* 使能ADCx时钟 */
//        ADC_ADCX_CHY_GPIO_CLK_ENABLE(); /* 开启GPIO时钟 */

//        /* AD采集引脚模式设置,模拟输入 */
//        gpio_init_struct.Pin = ADC_ADCX_CHY_GPIO_PIN;
//        gpio_init_struct.Mode = GPIO_MODE_ANALOG;
//        gpio_init_struct.Pull = GPIO_NOPULL;
//        HAL_GPIO_Init(ADC_ADCX_CHY_GPIO_PORT, &gpio_init_struct);
//    }
//}

/**
 * @brief       设置ADC通道采样时间和转换顺序
 * @param[in]   adc_handle  ADC句柄指针,指向ADC_HandleTypeDef结构体
 * @param[in]   ch          通道号,取值范围:
 *              @arg        ADC_CHANNEL_0~ADC_CHANNEL_17 (对应通道0~17)
 * @param[in]   rank        转换序列中的位置,取值范围:
 *              @arg        ADC_REGULAR_RANK_1~ADC_REGULAR_RANK_16 (序列位置1~16)
 * @param[in]   stime       采样时间,可选值:
 *              @arg        ADC_SAMPLETIME_3CYCLES   - 3个ADC时钟周期   (约0.14μs @21MHz)
 *              @arg        ADC_SAMPLETIME_15CYCLES  - 15个ADC时钟周期  (约0.71μs @21MHz)
 *              @arg        ADC_SAMPLETIME_28CYCLES  - 28个ADC时钟周期  (约1.33μs @21MHz)
 *              @arg        ADC_SAMPLETIME_56CYCLES  - 56个ADC时钟周期  (约2.67μs @21MHz)
 *              @arg        ADC_SAMPLETIME_84CYCLES  - 84个ADC时钟周期  (约4.00μs @21MHz)
 *              @arg        ADC_SAMPLETIME_112CYCLES - 112个ADC时钟周期 (约5.33μs @21MHz)
 *              @arg        ADC_SAMPLETIME_144CYCLES - 144个ADC时钟周期 (约6.86μs @21MHz)
 *              @arg        ADC_SAMPLETIME_480CYCLES - 480个ADC时钟周期 (约22.86μs @21MHz)
 * @note        该函数用于配置指定ADC通道的采样参数
 *              - rank参数在多通道DMA模式下决定数据在缓冲区中的位置
 *              - 例如:channel1的rank=1对应AdcDMA[0],channel2的rank=2对应AdcDMA[1]
 *              - 采样时间越长,转换精度越高,但转换速度越慢
 *              - 对于高阻抗信号源,建议使用较长的采样时间
 * @retval      无
 */
void adc_channel_set(ADC_HandleTypeDef *adc_handle, uint32_t ch, uint32_t rank, uint32_t stime)
{
    /* 配置对应ADC通道 */
    ADC_ChannelConfTypeDef adc_channel;
    
    adc_channel.Channel = ch;               /* 设置ADCX对通道ch */
    adc_channel.Rank = rank;                /* 设置采样序列 */
    adc_channel.SamplingTime = stime;       /* 设置采样时间 */
    HAL_ADC_ConfigChannel(adc_handle, &adc_channel); /* 初始化ADC通道 */
}

/**
 * @brief       获得ADC转换后的结果
 * @param[in]   ch: 通道值，取值范围:
 *              @arg        ADC_CHANNEL_0~ADC_CHANNEL_17 (对应通道0~17)
 * @retval      ADC转换结果
 */
uint32_t adc_get_result(uint32_t ch)
{
    adc_channel_set(&g_adc_handle, ch, 1, ADC_SAMPLETIME_480CYCLES);    /* 设置通道，序列和采样时间 */
    HAL_ADC_Start(&g_adc_handle);                                       /* 开启ADC */
    HAL_ADC_PollForConversion(&g_adc_handle, 10);                       /* 轮询转换 */
    return (uint16_t)HAL_ADC_GetValue(&g_adc_handle);                   /* 返回最近一次ADC1规则组的转换结果 */
}

/**
 * @brief       获取通道ch的转换值, 取times次, 然后平均
 * @param[in]   ch: 通道号，取值范围:
 *              @arg        ADC_CHANNEL_0~ADC_CHANNEL_17 (对应通道0~17)
 * @param[in]   times: 获取次数，范围1~255
 * @retval      通道ch的times次转换结果平均值
 */
uint32_t adc_get_result_average(uint32_t ch, uint8_t times)
{
    uint32_t temp_val = 0;
    uint8_t t;

    for (t = 0; t < times; t++)     /* 获取times次数据 */
    {
        temp_val += adc_get_result(ch);
        delay_ms(5);
    }

    return temp_val / times;        /* 返回平均值 */
}

/* ---------------------------------------- 单通道ADC采集(DMA读取)程序 ----------------------------------- */

/**
 * @brief       ADC DMA读取 初始化函数
 * @note        本函数还是使用adc_init对ADC进行大部分配置,有差异的地方再单独配置
 * @param[in]   mar: 存储器地址，用于存储DMA传输的数据
 * @retval      无
 */
void adc_dma_init(uint32_t mar)
{
    if ((uint32_t)ADC_ADCX_DMASx > (uint32_t)DMA2)      /* 大于DMA1_Stream7, 则为DMA2的通道了 */
    {
        __HAL_RCC_DMA2_CLK_ENABLE();                    /* DMA2时钟使能 */
    }
    else 
    {
        __HAL_RCC_DMA1_CLK_ENABLE();                    /* DMA1时钟使能 */
    }

    /* DMA配置 */
    g_dma_adc_handle.Instance = ADC_ADCX_DMASx;                             /* 设置DMA数据流 */
    g_dma_adc_handle.Init.Channel = DMA_CHANNEL_0;                          /* 设置DMA通道 */
    g_dma_adc_handle.Init.Direction = DMA_PERIPH_TO_MEMORY;                 /* DIR = 1 ,  外设到存储器模式 */
    g_dma_adc_handle.Init.PeriphInc = DMA_PINC_DISABLE;                     /* 外设非增量模式 */
    g_dma_adc_handle.Init.MemInc = DMA_MINC_ENABLE;                         /* 存储器增量模式 */
    g_dma_adc_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;    /* 外设数据长度:16位 */
    g_dma_adc_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;       /* 存储器数据长度:16位 */
    g_dma_adc_handle.Init.Mode = DMA_NORMAL;                                /* 外设流控模式 */
    g_dma_adc_handle.Init.Priority = DMA_PRIORITY_MEDIUM;                   /* 中等优先级 */
    HAL_DMA_Init(&g_dma_adc_handle);                                        /* 初始化DMA */
    HAL_DMA_Start(&g_dma_adc_handle, (uint32_t)&ADC1->DR, mar, 0);          /* 配置DMA传输参数 */

    g_adc_handle.DMA_Handle = &g_dma_adc_handle;    /* 配置ADC对应的DMA */

    adc_init();     /* ADC初始化 */
    
    /* 需要在配置的时候开，但这里为了保证不变更之前的代码，
     * 另加一行设置g_adc_handle.Init.ContinuousConvMode = ENABLE;
     * 配置ADC连续转换, DMA传输ADC数据 */
    SET_BIT(g_adc_handle.Instance->CR2, ADC_CR2_CONT);  /* CONT = 1, 连续转换模式 */

    /* 配置对应ADC通道和采样序列 */
    adc_channel_set(&g_adc_handle , ADC_ADCX_CHY, 1, ADC_SAMPLETIME_480CYCLES); 
    
    HAL_NVIC_SetPriority(ADC_ADCX_DMASx_IRQn, 3, 3);    /* 设置中断优先级为3，子优先级3 */
    HAL_NVIC_EnableIRQ(ADC_ADCX_DMASx_IRQn);            /* 使能DMA中断 */
    HAL_ADC_Start_DMA(&g_adc_handle, &mar, sizeof(uint16_t));   /* 开始DMA中断传输 */
    __HAL_DMA_ENABLE_IT(&g_dma_adc_handle, DMA_IT_TC);  /* TCIE=1, 使能传输完成中断 */
}

/**
 * @brief       使能一次ADC DMA传输 
 * @param[in]   ndtr: DMA传输的次数，即要传输的数据量
 * @retval      无
 */
void adc_dma_enable(uint16_t ndtr)
{
    __HAL_ADC_DISABLE(&g_adc_handle);               /* 先关闭ADC */
    
    __HAL_DMA_DISABLE(&g_dma_adc_handle);           /* 关闭DMA传输 */
    g_dma_adc_handle.Instance->NDTR = ndtr;         /* 重设DMA传输数据量 */
    __HAL_DMA_ENABLE(&g_dma_adc_handle);            /* 开启DMA传输 */
    
    __HAL_ADC_ENABLE(&g_adc_handle);                /* 重新启动ADC */
    ADC_ADCX->CR2 |= 1 << 30;                       /* 启动规则转换通道 */
}

/* ---------------------------------------- 多通道ADC采集(DMA读取)程序 ----------------------------------- */

ADC_HandleTypeDef g_adc_nch_dma_handle;     /* 与DMA关联的ADC句柄 */
DMA_HandleTypeDef g_dma_nch_adc_handle;     /* 与ADC关联的DMA句柄 */

/**
 * @brief       ADC N通道(6通道) DMA读取 初始化函数
 * @note        本函数还是使用 ADC_ADCX(ADC1) 和 ADC_ADCX_DMASx(DMA2_Stream4) 及其相关定义
 *              不要乱修改adc.h里面的这两部分内容, 必须在理解原理的基础上进行修改, 否则可能导致无法正常使用.
 * @param[in]   mar: 存储器地址，用于存储DMA传输的数据
 * @retval      无
 */
void adc_nch_dma_init(uint32_t mar)
{
    ADC_ADCX_CHY_CLK_ENABLE();                          /* 使能ADCx时钟 */
    
    if ((uint32_t)ADC_ADCX_DMASx > (uint32_t)DMA2)      /* 大于DMA1_Stream7, 则为DMA2 */
    {
        __HAL_RCC_DMA2_CLK_ENABLE();                    /* DMA2时钟使能 */
    }
    else
    {
        __HAL_RCC_DMA1_CLK_ENABLE();                    /* DMA1时钟使能 */
    }

    /* DMA配置 */
    g_dma_nch_adc_handle.Instance = ADC_ADCX_DMASx;                             /* 设置DMA数据流 */
    g_dma_nch_adc_handle.Init.Channel = DMA_CHANNEL_0;                          /* 设置DMA通道 */
    g_dma_nch_adc_handle.Init.Direction = DMA_PERIPH_TO_MEMORY;                 /* DIR = 1 , 外设到存储器模式 */
    g_dma_nch_adc_handle.Init.PeriphInc = DMA_PINC_DISABLE;                     /* 外设非增量模式 */
    g_dma_nch_adc_handle.Init.MemInc = DMA_MINC_ENABLE;                         /* 存储器增量模式 */
    g_dma_nch_adc_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;    /* 外设数据长度:16位 */
    g_dma_nch_adc_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;       /* 存储器数据长度:16位 */
    g_dma_nch_adc_handle.Init.Mode = DMA_NORMAL;                                /* 外设流控模式 */
    g_dma_nch_adc_handle.Init.Priority = DMA_PRIORITY_MEDIUM;                   /* 中等优先级 */
    HAL_DMA_Init(&g_dma_nch_adc_handle);                                        /* 初始化DMA */
    HAL_DMA_Start(&g_dma_nch_adc_handle, (uint32_t)&ADC_ADCX->DR, mar, 0);      /* 配置DMA传输参数 */

    g_adc_nch_dma_handle.DMA_Handle = &g_dma_nch_adc_handle;    /* 设置ADC对应的DMA */

    g_adc_nch_dma_handle.Instance = ADC_ADCX;
    g_adc_nch_dma_handle.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;            /* 4分频，ADCCLK = PCLK2/4 = 84/4 = 21Mhz */
    g_adc_nch_dma_handle.Init.Resolution = ADC_RESOLUTION_12B;                      /* 12位模式 */
    g_adc_nch_dma_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;                      /* 右对齐 */
    g_adc_nch_dma_handle.Init.ScanConvMode = ENABLE;                                /* 扫描模式 */
    g_adc_nch_dma_handle.Init.ContinuousConvMode = ENABLE;                          /* 连续转换模式，转换完成之后接着继续转换 */
    g_adc_nch_dma_handle.Init.DiscontinuousConvMode = DISABLE;                      /* 禁止不连续采样模式 */
    g_adc_nch_dma_handle.Init.NbrOfConversion = ADC_CH_NUM;                         /* 使用转换通道数，需根据实际转换通道去设置 */
    g_adc_nch_dma_handle.Init.NbrOfDiscConversion = 0;                              /* 不连续采样通道数为0 */
    g_adc_nch_dma_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;                /* 软件触发 */
    g_adc_nch_dma_handle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; /* 使用软件触发, 此位忽略 */
    g_adc_nch_dma_handle.Init.DMAContinuousRequests = ENABLE;                       /* 开启DMA连续转换 */
    HAL_ADC_Init(&g_adc_nch_dma_handle);                                            /* 初始化ADC */

    adc_nch_dma_gpio_init();    /* GPIO 初始化 */

    /* 设置采样规则序列1~9 */
    adc_channel_set(&g_adc_nch_dma_handle, ADC_CHANNEL_0, 1, ADC_SAMPLETIME_480CYCLES);
    adc_channel_set(&g_adc_nch_dma_handle, ADC_CHANNEL_1, 2, ADC_SAMPLETIME_480CYCLES);
    adc_channel_set(&g_adc_nch_dma_handle, ADC_CHANNEL_2, 3, ADC_SAMPLETIME_480CYCLES);
    adc_channel_set(&g_adc_nch_dma_handle, ADC_CHANNEL_3, 4, ADC_SAMPLETIME_480CYCLES);
    adc_channel_set(&g_adc_nch_dma_handle, ADC_CHANNEL_10, 5, ADC_SAMPLETIME_480CYCLES);
    adc_channel_set(&g_adc_nch_dma_handle, ADC_CHANNEL_11, 6, ADC_SAMPLETIME_480CYCLES);
    adc_channel_set(&g_adc_nch_dma_handle, ADC_CHANNEL_12, 7, ADC_SAMPLETIME_480CYCLES);
    adc_channel_set(&g_adc_nch_dma_handle, ADC_CHANNEL_13, 8, ADC_SAMPLETIME_480CYCLES);
    
    /* 开启内部参考电压 */
    ADC->CCR |= ADC_CCR_TSVREFE;  /* 使能内部温度传感器和VREFINT */
    adc_channel_set(&g_adc_nch_dma_handle, ADC_CHANNEL_VREFINT, 9, ADC_SAMPLETIME_480CYCLES);

    HAL_NVIC_SetPriority(ADC_ADCX_DMASx_IRQn, 3, 3);    /* 设置DMA中断优先级为3，子优先级为3 */
    HAL_NVIC_EnableIRQ(ADC_ADCX_DMASx_IRQn);            /* 使能DMA中断 */
    HAL_ADC_Start_DMA(&g_adc_nch_dma_handle, &mar, sizeof(uint16_t));   /* 开始DMA数据传输 */
    __HAL_DMA_ENABLE_IT(&g_dma_nch_adc_handle, DMA_IT_TC);              /* TCIE=1, 使能传输完成中断 */
}

/**
 * @brief       多通道ADC的GPIO初始化函数
 * @note        此函数会被adc_nch_dma_init()调用
 *              PA0-ADC_CHANNEL_0、PA1-ADC_CHANNEL_1、PA2-ADC_CHANNEL_2、PA3-ADC_CHANNEL_3
 *              PC0-ADC_CHANNEL_10、PC1-ADC_CHANNEL_11、PC2-ADC_CHANNEL_12、PC3-ADC_CHANNEL_13
 * @param       无
 * @retval      无
 */
void adc_nch_dma_gpio_init(void)
{
    GPIO_InitTypeDef gpio_init_struct;

    __HAL_RCC_GPIOA_CLK_ENABLE();                    /* 开启GPIOA引脚时钟 */
    __HAL_RCC_GPIOC_CLK_ENABLE();                    /* 开启GPIOC引脚时钟 */

    /* AD采集引脚模式设置,模拟输入 */
    /* PA0~PA3对应ADC通道0~3 */
    gpio_init_struct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3; /* GPIOA0~3 */
    gpio_init_struct.Mode = GPIO_MODE_ANALOG;
    gpio_init_struct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &gpio_init_struct);
    
    /* PC0~PC3对应ADC通道10~13 */
    gpio_init_struct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3; /* GPIOC0~3 */
    gpio_init_struct.Mode = GPIO_MODE_ANALOG;
    gpio_init_struct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOC, &gpio_init_struct);
}

/**
 * @brief       使能一次ADC DMA传输
 * @param[in]   ndtr: DMA传输的次数，即要传输的数据量
 * @retval      无
 */
void adc_nch_dma_enable(uint16_t ndtr)
{
    __HAL_ADC_DISABLE(&g_adc_nch_dma_handle);       /* 先关闭ADC */
    
    __HAL_DMA_DISABLE(&g_dma_nch_adc_handle);       /* 关闭DMA传输 */
    g_dma_nch_adc_handle.Instance->NDTR = ndtr;     /* 重设DMA传输数据量 */
    __HAL_DMA_ENABLE(&g_dma_nch_adc_handle);        /* 开启DMA传输 */
    
    __HAL_ADC_ENABLE(&g_adc_nch_dma_handle);        /* 重新启动ADC */
    ADC_ADCX->CR2 |= 1 << 30;                       /* 启动规则转换通道 */
}

/* -------------------------------------------- 中断服务函数 -------------------------------------------- */

/**
 * @brief       ADC DMA采集中断服务函数
 * @param       无
 * @retval      无
 */
void ADC_ADCX_DMASx_IRQHandler(void)
{
    if (ADC_ADCX_DMASx_IS_TC())
    {
        g_adc_dma_sta = 1;          /* 标记DMA传输完成 */
        ADC_ADCX_DMASx_CLR_TC();    /* 清除DMA2 数据流4 传输完成中断 */
    }
}

/* -------------------------------------------- ADC数据处理 -------------------------------------------- */

/**
 * @brief       一阶低通滤波函数
 * @param[in]   new_value: 新采集的数据值
 * @param[in]   channel: 通道号，用于区分不同通道的滤波状态
 * @retval      滤波后的值
 * @note        滤波公式: y(n) = α·x(n) + (1-α)·y(n-1)
 *              - 内部使用静态数组保存各通道上次滤波结果
 *              - 滤波系数alpha=0.2，可根据需要调整
 */
uint16_t adc_filter(uint16_t new_value, uint8_t channel)
{
    /* 静态变量，保存各通道的滤波状态 */
    static uint16_t last_values[ADC_CH_NUM] = {0};   /* 上一次的滤波结果 */
    static uint8_t is_initialized = 0;               /* 初始化标志 */
    const float alpha = 0.2f;                        /* 滤波系数，值越小滤波效果越强，但响应越慢 */
    
    /* 检查通道号是否有效 */
    if (channel >= ADC_CH_NUM)
    {
        return new_value; /* 通道号无效，直接返回原值 */
    }
    
    /* 第一次调用时，初始化所有通道的last_values */
    if (!is_initialized)
    {
        for (uint8_t i = 0; i < ADC_CH_NUM; i++)
        {
            last_values[i] = 0;
        }
        is_initialized = 1;
        
        /* 第一次运行，直接使用当前值 */
        last_values[channel] = new_value;
        return new_value;
    }
    
    /* 一阶低通滤波: y(n) = α·x(n) + (1-α)·y(n-1) */
    float filtered_value = alpha * (float)new_value + (1.0f - alpha) * (float)last_values[channel];
    
    /* 更新该通道的last_value */
    last_values[channel] = (uint16_t)filtered_value;
    
    return (uint16_t)filtered_value;
}

/* VDDA校准相关的静态变量 */
static uint8_t g_calibration_count = 0;
static uint16_t g_last_vrefint_value = 0;
static uint8_t g_stable_count = 0;

/**
 * @brief       VDDA电压校准函数
 * @param       无
 * @retval      无
 * @details     使用内部参考电压VREFINT校准VDDA电压值
 *              等待滤波器稳定后再进行校准，确保精度
 *              STM32F407内部参考电压VREFINT的典型值为1.21V
 *              计算公式: VDDA = VREFINT(典型值) * 4096 / VREFINT_DATA
 */
static void adc_calibrate_vdda(void)
{
    if (g_vdda_calibrated == 0)
    {
        g_calibration_count++;
        
        /* 方法1：最少等待10次，确保基本稳定 */
        if (g_calibration_count >= 10)
        {
            /* 方法2：检查VREFINT值是否稳定（连续3次变化小于5个ADC码） */
            uint16_t current_vrefint = g_adc_values[ADC_CHANNEL_VREFINT_INDEX];
            if (g_last_vrefint_value != 0)
            {
                uint16_t diff = (current_vrefint > g_last_vrefint_value) ? 
                               (current_vrefint - g_last_vrefint_value) : 
                               (g_last_vrefint_value - current_vrefint);
                
                if (diff <= 5)  /* 变化小于5个ADC码认为稳定 */
                {
                    g_stable_count++;
                }
                else
                {
                    g_stable_count = 0;  /* 重新开始计数 */
                }
            }
            
            g_last_vrefint_value = current_vrefint;
            
            /* 连续3次稳定才进行校准 */
            if (g_stable_count >= 3)
            {
                g_vdda_voltage = 1.21f * 4096 / current_vrefint; /* 使用1.21V作为VREFINT的典型值 */
                g_vdda_calibrated = 1; /* 标记已校准 */
            }
            else
            {
                /* 还未稳定，继续使用临时值 */
                g_vdda_voltage = 2.5f;
            }
        }
        else
        {
            /* 校准前使用标准2.5V作为临时VDDA值 */
            g_vdda_voltage = 2.5f;
        }
    }
    /* 后续使用固定的VDDA值，确保DAC输出稳定 */
}

/**
 * @brief       重新校准VDDA电压值
 * @param       无
 * @retval      无
 * @note        此函数用于在需要时重新校准VDDA值
 *              调用后，下次ADC数据处理时会重新计算VDDA值
 *              会重置所有校准相关的静态变量，确保重新开始校准过程
 */
void adc_recalibrate_vdda(void)
{
    g_vdda_calibrated = 0; /* 清除校准标志，下次处理时会重新校准 */
    
    /* 重置校准相关的静态变量 */
    g_calibration_count = 0;
    g_last_vrefint_value = 0;
    g_stable_count = 0;
}

/**
 * @brief       ADC数据处理函数
 * @param       无
 * @retval      uint8_t 返回1表示处理了新数据，返回0表示没有新数据
 * @details     处理ADC采集到的数据，计算VDDA和各通道电压值
 *              前四个通道(0-3)用于测量0-10V电压，实际电压为ADC测量值的4倍
 *              后四个通道(10-13)用于测量4-20mA电流，通过60Ω采样电阻，使用公式: I(mA) = V(V)*1000/60
 */
uint8_t adc_process_data(void)
{
    uint16_t i, j;
    uint32_t sum;
    
    /* 判断是否有新数据 */
    if (g_adc_dma_sta == 1)
    {
        /* 首先计算各通道的原始ADC值并进行滤波 */
        for(j = 0; j < ADC_CH_NUM; j++)
        {
            sum = 0; /* 清零 */
            for (i = 0; i < ADC_DMA_BUF_SIZE / ADC_CH_NUM; i++)
            {
                sum += g_adc_dma_buf[(ADC_CH_NUM * i) + j];  /* 相同通道的转换数据累加 */
            }
            
            g_adc_values[j] = sum / (ADC_DMA_BUF_SIZE / ADC_CH_NUM);  /* 取平均值 */
            
            /* 对ADC值进行滤波处理 */
            g_adc_values[j] = adc_filter(g_adc_values[j], j);
        }
        
        /* 校准VDDA电压值 */
        adc_calibrate_vdda();
        
        /* 计算各通道电压值和实际物理量 */
        for (j = 0; j < ADC_CH_NUM; j++)
        {
            /* 先计算基础电压值 - 存入g_adc_voltages */
            g_adc_voltages[j] = (float)g_adc_values[j] * (g_vdda_voltage / 4096);
            
            /* 根据硬件电路特性计算实际物理量 - 存入g_actual_values */
            if (j <= ADC_CHANNEL_3_INDEX) /* 前四个通道(0-3)用于测量0-10V电压 */
            {
                /* 电压值乘以4，得到实际测量的电压(0-10V) */
                g_actual_values[j] = g_adc_voltages[j] * 4.0f;
            }
            else if (j >= ADC_CHANNEL_10_INDEX && j <= ADC_CHANNEL_13_INDEX) /* 后四个通道(10-13)用于测量4-20mA电流 */
            {
                /* 将电压值转换为电流值(mA)
                 * 转换公式: 电流(A) = 电压(V) / 电阻(Ω)
                 * 由于使用了60Ω采样电阻，且需要显示mA单位，所以:
                 * 电流(mA) = (电压(V) / 60Ω) * 1000 = 电压(V) * 1000 / 60 */
                g_actual_values[j] = g_adc_voltages[j] * 1000.0f / 60.0f;
            }
            else /* VREFINT通道 - 拷贝电压值 */
            {
                g_actual_values[j] = g_adc_voltages[j];
            }
        }
        
        /* 处理完成后，重新启动DMA采集 */
        g_adc_dma_sta = 0;  /* 清除DMA采集完成状态标志 */
        adc_nch_dma_enable(ADC_DMA_BUF_SIZE);   /* 启动下一次ADC DMA多通道采集 */
        
        return 1; /* 返回1表示处理了新数据 */
    }
    
    return 0; /* 返回0表示没有新数据 */
}
